fix(filesystem): use platform specific API to make a file read-only
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Fri, 25 Apr 2025 08:11:15 +0000 (10:11 +0200)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Mon, 28 Apr 2025 09:46:41 +0000 (09:46 +0000)
on Windows we may fail to mark a file read-only be read-write again
using high level API

switch to use of low level C API from Microsoft

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
src/common/filesystembase.cpp

index 88ace9a91b862f7fc792aed0949f2c8bd19608a5..d2497a3017a544319627482178e54b4653a38936 100644 (file)
@@ -111,36 +111,43 @@ static QFile::Permissions getDefaultWritePermissions()
 void FileSystem::setFileReadOnly(const QString &filename, bool readonly)
 {
 #ifdef  Q_OS_WIN
-    if (isLnkFile(filename)) {
-        if (!fileExists(filename)) {
-            return;
-        }
-        try {
-            const auto permissions = filePermissionsWin(filename);
+    if (!fileExists(filename)) {
+        Q_ASSERT(false);
+        return;
+    }
 
-            std::filesystem::perms allWritePermissions = std::filesystem::perms::_All_write;
-            static std::filesystem::perms defaultWritePermissions = std::filesystem::perms::others_write;
+    const auto fileAttributes = GetFileAttributesW(filename.toStdWString().c_str());
+    if (fileAttributes == INVALID_FILE_ATTRIBUTES) {
+        const auto lastError = GetLastError();
+        auto errorMessage = static_cast<char*>(nullptr);
+        if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                          nullptr, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, 0, nullptr) == 0) {
+            qCWarning(lcFileSystem()) << "GetFileAttributesW" << filename << (readonly ? "readonly" : "read write") << errorMessage;
+        } else {
+            qCWarning(lcFileSystem()) << "GetFileAttributesW" << filename << (readonly ? "readonly" : "read write") << "unknown error" << lastError;
+        }
+        return;
+    }
 
-            std::filesystem::permissions(filename.toStdWString(), allWritePermissions, std::filesystem::perm_options::remove);
+    auto newFileAttributes = fileAttributes;
+    if (readonly) {
+        newFileAttributes = newFileAttributes | FILE_ATTRIBUTE_READONLY;
+    } else {
+        newFileAttributes = newFileAttributes & (~FILE_ATTRIBUTE_READONLY);
+    }
 
-            if (!readonly) {
-                std::filesystem::permissions(filename.toStdWString(), defaultWritePermissions, std::filesystem::perm_options::add);
-            }
-        }
-        catch (const std::filesystem::filesystem_error &e)
-        {
-            qCWarning(lcFileSystem()) << filename << (readonly ? "readonly" : "read write") << e.what();
-        }
-        catch (const std::system_error &e)
-        {
-            qCWarning(lcFileSystem()) << filename << e.what();
-        }
-        catch (...)
-        {
-            qCWarning(lcFileSystem()) << filename;
+    if (SetFileAttributesW(filename.toStdWString().c_str(), newFileAttributes) == 0) {
+        const auto lastError = GetLastError();
+        auto errorMessage = static_cast<char*>(nullptr);
+        if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                           nullptr, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMessage, 0, nullptr) == 0) {
+            qCWarning(lcFileSystem()) << "SetFileAttributesW" << filename << (readonly ? "readonly" : "read write") << errorMessage;
+        } else {
+            qCWarning(lcFileSystem()) << "SetFileAttributesW" << filename << (readonly ? "readonly" : "read write") << "unknown error" << lastError;
         }
-        return;
     }
+
+    return;
 #endif
     QFile file(filename);
     QFile::Permissions permissions = file.permissions();